package com.ruoyi.system.service.impl;

import com.alibaba.fastjson2.JSONArray;
import com.ghgande.j2mod.modbus.procimg.Register;
import com.ruoyi.common.bean.Domain;
import com.ruoyi.common.enums.DeviceStatus;
import com.ruoyi.common.enums.ProductEnum;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.modbus.J2modUtils;
import com.ruoyi.common.utils.modbus.Modbus4jUtils;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.domain.vo.DeviceMonitorReponse;
import com.ruoyi.system.domain.vo.DeviceMonitorRequest;
import com.ruoyi.system.mapper.*;
import com.ruoyi.system.service.IMrMonitorDevicePointValueService;
import com.ruoyi.system.service.SensorDataService;
import com.ruoyi.system.utils.ClassMethodNameUtil;
import com.ruoyi.system.utils.LogUtil;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 设备监控项-监控数据Service业务层处理
 *
 * @author lsw
 * @date 2022-12-04
 */
@Service
public class MrMonitorDevicePointValueServiceImpl implements IMrMonitorDevicePointValueService {
    @Autowired
    private MrMonitorDevicePointValueMapper mrMonitorDevicePointValueMapper;

    @Autowired
    private MrDeviceMapper mrDeviceMapper;

    @Autowired
    private MrDeviceGraphicalConfigurationMapper mrDeviceGraphicalConfigurationMapper;

    @Autowired
    private SysDictDataMapper sysDictDataMapper;

    @Autowired
    private SensorDataService sensorDataService;

    @Autowired
    private LogUtil logUtil;

    private static final String DEVICE_RANGE = "device_range";

    static Log log = LogFactory.getLog(MrMonitorDevicePointValueServiceImpl.class);

    /**
     * 查询设备监控项-监控数据
     *
     * @param autoId 设备监控项-监控数据主键
     * @return 设备监控项-监控数据
     */
    @Override
    public MrMonitorDevicePointValue selectMrMonitorDevicePointValueByAutoId(Long autoId) {
        return mrMonitorDevicePointValueMapper.selectMrMonitorDevicePointValueByAutoId(autoId);
    }

    /**
     * 查询设备监控项-监控数据列表
     *
     * @param mrMonitorDevicePointValue 设备监控项-监控数据
     * @return 设备监控项-监控数据
     */
    @Override
    public List<MrMonitorDevicePointValue> selectMrMonitorDevicePointValueList(MrMonitorDevicePointValue mrMonitorDevicePointValue) {
        return mrMonitorDevicePointValueMapper.selectMrMonitorDevicePointValueList(mrMonitorDevicePointValue);
    }

    /**
     * 新增设备监控项-监控数据
     *
     * @param mrMonitorDevicePointValue 设备监控项-监控数据
     * @return 结果
     */
    @Override
    public int insertMrMonitorDevicePointValue(MrMonitorDevicePointValue mrMonitorDevicePointValue) {
        return mrMonitorDevicePointValueMapper.insertMrMonitorDevicePointValue(mrMonitorDevicePointValue);
    }

    /**
     * 修改设备监控项-监控数据
     *
     * @param mrMonitorDevicePointValue 设备监控项-监控数据
     * @return 结果
     */
    @Override
    public int updateMrMonitorDevicePointValue(MrMonitorDevicePointValue mrMonitorDevicePointValue) {
        return mrMonitorDevicePointValueMapper.updateMrMonitorDevicePointValue(mrMonitorDevicePointValue);
    }

    /**
     * 批量删除设备监控项-监控数据
     *
     * @param autoIds 需要删除的设备监控项-监控数据主键
     * @return 结果
     */
    @Override
    public int deleteMrMonitorDevicePointValueByAutoIds(Long[] autoIds) {
        return mrMonitorDevicePointValueMapper.deleteMrMonitorDevicePointValueByAutoIds(autoIds);
    }

    /**
     * 删除设备监控项-监控数据信息
     *
     * @param autoId 设备监控项-监控数据主键
     * @return 结果
     */
    @Override
    public int deleteMrMonitorDevicePointValueByAutoId(Long autoId) {
        return mrMonitorDevicePointValueMapper.deleteMrMonitorDevicePointValueByAutoId(autoId);
    }

    /**
     * 查询设备监控项数据，用于绘制趋势图
     */
    @Override
    public DeviceMonitorReponse trendChartData(DeviceMonitorRequest request) {
        String nowTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, new Date());
        String startTime = "2023-10-10 00:00:00";
        request.setStartTime(startTime);
        String endTime = nowTime + " 23:59:59";
        request.setEndTime(endTime);
        DeviceMonitorReponse deviceMonitorReponse = new DeviceMonitorReponse();
        JSONArray data = new JSONArray();
        JSONArray xData = new JSONArray();
        JSONArray values;
        MrDeviceGraphicalConfiguration deviceGraphicalConfiguration = mrDeviceGraphicalConfigurationMapper.selectMrDeviceGraphicalConfigurationById(request.getDeviceGraphicalId());
        deviceMonitorReponse.setUnit(deviceGraphicalConfiguration.getUnit());
        deviceMonitorReponse.setTime(deviceGraphicalConfiguration.getTime());
        Map<String, String> colorMap = new HashMap<>();
        if (StringUtils.isNotEmpty(deviceGraphicalConfiguration.getColor())) {
            String[] colors = deviceGraphicalConfiguration.getColor().split(",");
            for (String c : colors) {
                String[] strings = c.split(":");
                colorMap.put(strings[0], strings[1]);
            }
        }
        if (StringUtils.isNotEmpty(deviceGraphicalConfiguration.getRanges())) {
            String label = sysDictDataMapper.selectDictLabel(DEVICE_RANGE, deviceGraphicalConfiguration.getRanges());
            if (StringUtils.isNotEmpty(label)) {
                String[] strings = label.split("~");
                deviceMonitorReponse.setMinValue(new BigDecimal(strings[0]));
                deviceMonitorReponse.setMaxValue(new BigDecimal(strings[1]));
            }
        }

        MrDevice mrDevice = new MrDevice();
        String[] strDeviceIds = deviceGraphicalConfiguration.getDeviceIds().split(",");
        mrDevice.setStrDeviceIds(strDeviceIds);
        List<MrDevice> mrDevices = mrDeviceMapper.selectMrDeviceList(mrDevice);
        Map<Long, MrDevice> mrDeviceMap = mrDevices.stream()
                .collect(Collectors.toMap(
                        MrDevice::getDeviceId, Function.identity()
                ));
        for (String string : strDeviceIds) {
            MrDevice device = mrDeviceMap.get(Long.parseLong(string));
            request.setBoxCode(device.getSystemId());
            request.setPointCode(device.getUnit());
            //查询监控数据
            List<MrMonitorDevicePointValue> list = mrMonitorDevicePointValueMapper.selectMetricDara(request);
            if (list != null && !list.isEmpty()) {
                values = new JSONArray();
                xData = new JSONArray();
                for (MrMonitorDevicePointValue m : list) {
                    xData.add(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, m.getMetricTime()));
                    values.add(m.getMetricValue());
                }
                Map<String, String> map = new HashMap<>();
                map.put("pointName", device.getSystemId());
                map.put("deviceName", device.getDeviceName());
                map.put("color", colorMap.get(device.getDeviceId().toString()));
                map.put("value", values.toJSONString());
                data.add(map);
            }
        }
        deviceMonitorReponse.setData(data);
        deviceMonitorReponse.setxData(xData);
        return deviceMonitorReponse;
    }

    /**
     * 查询设备监控项数据，用于绘制柱状图
     */
    @Override
    public DeviceMonitorReponse histogramData(DeviceMonitorRequest request) {
        DeviceMonitorReponse deviceMonitorReponse = new DeviceMonitorReponse();
        JSONArray data = new JSONArray();
        MrDeviceGraphicalConfiguration deviceGraphicalConfiguration = mrDeviceGraphicalConfigurationMapper.selectMrDeviceGraphicalConfigurationById(request.getDeviceGraphicalId());
        if (deviceGraphicalConfiguration == null) {
            return null;
        }
        deviceMonitorReponse.setUnit(deviceGraphicalConfiguration.getUnit());
        deviceMonitorReponse.setTime(deviceGraphicalConfiguration.getTime());
        deviceMonitorReponse.setPointName(deviceGraphicalConfiguration.getName());
        if (StringUtils.isNotEmpty(deviceGraphicalConfiguration.getRanges())) {
            String label = sysDictDataMapper.selectDictLabel(DEVICE_RANGE, deviceGraphicalConfiguration.getRanges());
            if (StringUtils.isNotEmpty(label)) {
                String[] strings = label.split("~");
                deviceMonitorReponse.setMinValue(new BigDecimal(strings[0]));
                deviceMonitorReponse.setMaxValue(new BigDecimal(strings[1]));
            }
        }
        MrDevice mrDevice = new MrDevice();

        String[] strDeviceIds = deviceGraphicalConfiguration.getDeviceIds().split(",");
        mrDevice.setStrDeviceIds(strDeviceIds);
        List<MrDevice> mrDevices = mrDeviceMapper.selectMrDeviceList(mrDevice);
        Map<Long, MrDevice> mrDeviceMap = mrDevices.stream()
                .collect(Collectors.toMap(
                        MrDevice::getDeviceId, Function.identity()
                ));
        for (String string : strDeviceIds) {
            MrDevice device = mrDeviceMap.get(Long.parseLong(string));
            if (device == null) {
                continue;
            }
            Map<String, String> map = new HashMap<>();
            map.put("pointName", device.getSystemId());
            map.put("deviceName", device.getDeviceName());
            map.put("value", device.getRemark());
            data.add(map);
        }
        deviceMonitorReponse.setData(data);
        return deviceMonitorReponse;
    }

    /**
     * 查询设备监控项数据，用于绘制模拟图
     */
    @Override
    public DeviceMonitorReponse simulationDiagramData(DeviceMonitorRequest request) {
        DeviceMonitorReponse deviceMonitorReponse = new DeviceMonitorReponse();
        JSONArray data = new JSONArray();
        MrDeviceGraphicalConfiguration deviceGraphicalConfiguration = mrDeviceGraphicalConfigurationMapper.selectMrDeviceGraphicalConfigurationById(request.getDeviceGraphicalId());
        MrDevice mrDevice = new MrDevice();
        mrDevice.setStrDeviceIds(deviceGraphicalConfiguration.getDeviceIds().split(","));
        List<MrDevice> mrDevices = mrDeviceMapper.selectMrDeviceList(mrDevice);
        for (MrDevice device : mrDevices) {
            Map<String, String> map = new HashMap<>();
            map.put("pointName", device.getSystemId());
            map.put("deviceName", device.getDeviceName());
            map.put("unit", device.getUnit());
            map.put("value", device.getRemark());
            data.add(map);
        }
        deviceMonitorReponse.setData(data);
        return deviceMonitorReponse;
    }

    /**
     * 批量保存上行数据
     *
     * @param mrMonitorDevicePointValues 数据
     */
    @Override
    public void bathInsertMrMonitorDevicePointValue(List<MrMonitorDevicePointValue> mrMonitorDevicePointValues) {
        mrMonitorDevicePointValueMapper.bathInsertMrMonitorDevicePointValue(mrMonitorDevicePointValues);
    }

    /**
     * 设备上行数据清理
     */
    @Override
    public void cleanDeviceData() {
        mrMonitorDevicePointValueMapper.deleteMrMonitorDevicePointValue();
    }

    /**
     * 单点获取设备数据
     *
     * @param host IP
     * @param port 端口
     */
    @Override
    public void getDeviceData2(String host, String port) {
        // 1.数据获取
        List<Domain> domainList = getDomainList2(host, port);

        // 2.数据处理
        sensorDataService.dataProcessing(domainList);

    }

    private List<Domain> getDomainList2(String host, String port) {
        List<Domain> domainList = new ArrayList<>();
        // 获取设备信息
        MrDevice mrDevice = new MrDevice();
        mrDevice.setStatus(DeviceStatus.ACTIVE.getValue());
        mrDevice.setHost(host);
        mrDevice.setPort(port);
        List<MrDevice> mrDevices = mrDeviceMapper.selectMrDeviceList(mrDevice);
        mrDevices = mrDevices.stream().filter(device -> StringUtils.isNotEmpty(device.getRegisterAddress())).collect(Collectors.toList());
//        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
//        for (MrDevice mr : mrDevices) {
//            getDomains(host, port, mr, map, domainList);
//        }
        getBathDomains(host, port, mrDevices, domainList);
        return domainList;
    }

    /**
     * 多线程批量获取
     * @param host IP
     * @param port 端口
     * @param mrDevices 数据集
     * @param domainList 结果集合
     */
    private void getBathDomains(String host, String port, List<MrDevice> mrDevices, List<Domain> domainList) {
        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
        // 创建线程池（根据任务数量动态调整大小）
        int threadCount = Math.min(mrDevices.size(), Runtime.getRuntime().availableProcessors() * 2);
        ExecutorService executor = Executors.newFixedThreadPool(threadCount);

        // 创建任务列表
        List<Callable<Void>> tasks = new ArrayList<>();
        for (MrDevice mr : mrDevices) {
            tasks.add(() -> {
                getDomains(host, port, mr, map, domainList);
                return null;
            });
        }
        try {
            // 批量执行所有任务并等待完成
            executor.invokeAll(tasks);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.err.println("批量获取模块数据被中断: " + e.getMessage());
        } finally {
            // 关闭线程池
            executor.shutdown();
            try {
                // 等待所有任务完成（最多10秒）
                if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
                    executor.shutdownNow();
                }
            } catch (InterruptedException e) {
                executor.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
    }

    private void getDomains(String host, String port, MrDevice mr, ConcurrentHashMap<String, String> map, List<Domain> domainList) {
        String value = null;
        Domain domain = new Domain();
        domain.setPoint_code(mr.getSystemId());
        domain.setPort(port);

        try {
            value = getValue(host, port, mr, map, value);
        } catch (ModbusTransportException | ErrorResponseException | ModbusInitException e) {
            // 通道报警
            sensorDataService.channelAlarm(Integer.parseInt(port));

            String msg = "【getDomainList2】【通道报警】: " + e.getMessage();
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
        }
        if (value != null) {
            // 网关数据协议处理
            value = sensorDataService.processData(mr, value);
            domain.setValue(value);
            domainList.add(domain);
//            sensorDataService.dataProcessing(domainList);
//            domainList = new ArrayList<>();
        }
    }

    /**
     * 获取模块数据
     * @return 值
     */
    private String getValue(String host, String port, MrDevice mr, ConcurrentHashMap<String, String> map, String value) throws ModbusInitException, ModbusTransportException, ErrorResponseException {
        int slaveId = 1;
        boolean flag;
        Number number = null;
        String msg;
        int offset = Integer.parseInt(mr.getRegisterAddress());
        if (mr.getModality().equals(ProductEnum.BA.getValue())) {
            switch (mr.getProduct()) {
                case "4-20mA":
                case "4-20mA输出":
                case "PT100":
                case "0-10V":
                case "非设备硬接点模块":
                    number = Modbus4jUtils.readHoldingRegister(host, Integer.parseInt(port), slaveId, offset, DataType.TWO_BYTE_INT_UNSIGNED);
                    break;
                case "MODBUS":
                case "MODBUS_FLOAT":
                    // TODO 13500 使用 DataType.FOUR_BYTE_FLOAT
                    number = Modbus4jUtils.readHoldingRegister(host, Integer.parseInt(port), slaveId, offset, DataType.FOUR_BYTE_FLOAT);
                    msg = "【getDomainList2】设备上行数据,host: " + host + ", port: " + port + ", slaveId: " + slaveId + ", offset: " + offset + ", value: " + number;
                    logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
                    break;
                default:
                    msg = "【getDomainList2】设备上行数据,设备类型未识别 product: " + mr.getProduct() + ",systemId:" + mr.getSystemId();
                    logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
            }
            if (number != null) {
                value = number.toString();
            }
        }
        if (mr.getModality().equals(ProductEnum.MR.getValue())) {
            switch (mr.getProduct()) {
                case "开关A-0":
                    flag = Modbus4jUtils.readInputStatus(host, Integer.parseInt(port), slaveId, offset);
                    value = String.valueOf(flag);
                    break;
                case "device":
                    String key = host + port + slaveId + offset;
                    if (map.containsKey(key)) {
                        value = map.get(key);
                    } else {
                        number = Modbus4jUtils.readHoldingRegister(host, Integer.parseInt(port), slaveId, offset, DataType.TWO_BYTE_INT_UNSIGNED);
                        msg = "【getDomainList2】设备上行数据,host: " + host + ", port: " + port + ", slaveId: " + slaveId + ", offset: " + offset + ", value: " + number;
                        logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
                        if (number != null) {
                            value = number.toString();
                            map.put(key, value);
                        }
                    }
                    break;
                default:
                    msg = "【getDomainList2】设备上行数据,设备类型未识别 product: " + mr.getProduct() + ",systemId:" + mr.getSystemId();
                    logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
            }
        }

        return value;
    }


    /**
     * 批量获取设备数据
     *
     * @param host IP
     * @param port 端口
     */
    @Override
    public void getDeviceData3(String host, String port) {
        // 1.数据获取
        List<Domain> domainList = getDomainList3(host, port);

        // 2.数据处理
        sensorDataService.dataProcessing(domainList);

    }

    private List<Domain> getDomainList3(String host, String port) {
        List<Domain> domainList = new ArrayList<>();
        Map<Integer, Boolean> mapInput;
        Map<Integer, Register> mapInput2;
        Map<Integer, Register> mapMultiple;

        MrDevice mrDevice = new MrDevice();
        mrDevice.setStatus(DeviceStatus.ACTIVE.getValue());
        mrDevice.setHost(host);
        mrDevice.setPort(port);
        mrDevice.setModality(ProductEnum.MR.getValue());
        mrDevice.setProduct(ProductEnum.TYPE_MR_A_0.getValue());
        try {
            mapInput = getMapInput(host, port, mrDevice);

            mrDevice.setProduct(ProductEnum.TYPE_DEVICE.getValue());
            mapInput2 = getMapMultiple(host, port, mrDevice);

            mrDevice.setModality(ProductEnum.BA.getValue());
            mrDevice.setProduct(null);
            mapMultiple = getMapMultiple(host, port, mrDevice);
        } catch (Exception e) {
            // 通道报警
            sensorDataService.channelAlarm(Integer.parseInt(port));
            return domainList;
        }

        // 获取设备信息
        mrDevice.setModality(null);
        mrDevice.setProduct(null);
        List<MrDevice> mrDevices = mrDeviceMapper.selectMrDeviceList(mrDevice);
        for (MrDevice mr : mrDevices) {
            if (checkGetDomainList3(mr, mapInput, mapInput2, mapMultiple)) {
                continue;
            }
            int offset = Integer.parseInt(mr.getRegisterAddress());
            boolean flag;
            String value = null;
            Domain domain = new Domain();
            domain.setPoint_code(mr.getSystemId());
            domain.setPort(port);
            if (mr.getModality().equals(ProductEnum.BA.getValue())) {
                value = mapMultiple.get(offset).toString();
            }
            if (mr.getModality().equals(ProductEnum.MR.getValue())) {
                // 开关A-0
                if (mr.getProduct().equals(ProductEnum.TYPE_MR_A_0.getValue())) {
                    flag = mapInput.get(offset);
                    value = String.valueOf(flag);
                }
                // 设备开关
                if (mr.getProduct().equals(ProductEnum.TYPE_DEVICE.getValue())) {
                    value = mapInput2.get(offset).toString();
                }
            }
            // 网关数据协议处理
            value = sensorDataService.processData(mr, value);
            domain.setValue(value);
            domainList.add(domain);
        }
        return domainList;
    }

    /**
     * 参数校验
     */
    private boolean checkGetDomainList3(MrDevice mr, Map<Integer, Boolean> mapInput, Map<Integer, Register> mapInput2, Map<Integer, Register> mapMultiple) {
        if (StringUtils.isEmpty(mr.getRegisterAddress())) {
            return true;
        }
        if (mr.getModality().equals(ProductEnum.BA.getValue())) {
            return mapMultiple == null;
        }
        if (mr.getModality().equals(ProductEnum.MR.getValue())) {
            // 开关A-0
            if (mr.getProduct().equals(ProductEnum.TYPE_MR_A_0.getValue())) {
                return mapInput == null;
            }
            // 设备开关
            if (mr.getProduct().equals(ProductEnum.TYPE_DEVICE.getValue())) {
                return mapInput2 == null;
            }
        }

        return false;
    }

    /**
     * 批量获取模拟量
     */
    private Map<Integer, Register> getMapMultiple(String host, String port, MrDevice mrDevice) throws Exception {
        // 设定从站ID
        int slaveId = 1;
        // 起始地址，从0开始即为第1个寄存器
        Integer startAddress;
        // 读取的寄存器数量，从第1个寄存器开始读取9个寄存器
        Integer registerQuantity;
        Map<Integer, Register> mapMultiple = null;
        startAddress = mrDeviceMapper.getRegisterAddressMinValue(mrDevice);
        if (startAddress != null) {
            registerQuantity = mrDeviceMapper.getRegisterAddressMaxValue(mrDevice);
            registerQuantity = registerQuantity - startAddress + 1;
            // 开关量 设备开关
            mapMultiple = J2modUtils.readMultipleRegisters(host, Integer.parseInt(port), slaveId, startAddress, registerQuantity);
        }
        return mapMultiple;
    }

    /**
     * 批量获取开关量
     */
    private Map<Integer, Boolean> getMapInput(String host, String port, MrDevice mrDevice) throws Exception {
        // 设定从站ID
        int slaveId = 1;
        // 起始地址，从0开始即为第1个寄存器
        Integer startAddress;
        // 读取的寄存器数量，从第1个寄存器开始读取9个寄存器
        Integer registerQuantity;
        Map<Integer, Boolean> mapInput = null;
        startAddress = mrDeviceMapper.getRegisterAddressMinValue(mrDevice);
        if (startAddress != null) {
            registerQuantity = mrDeviceMapper.getRegisterAddressMaxValue(mrDevice);
            registerQuantity = registerQuantity - startAddress + 1;
            // 开关量 开关A-0
            mapInput = J2modUtils.readInputDiscretes(host, Integer.parseInt(port), slaveId, startAddress, registerQuantity);
        }
        return mapInput;
    }

}
